/*
 * -------------------------------------- 
 * FileName: 2.stack.c 
 * Content:
 * 
 * Auther:  杨则瑞 
 * Encode:  utf-8 | CRLF                  
 * -------------------------------------- 
 */

#include<stdio.h>
#include<stack.h>
#include<dual_list.h>
#include<malloc.h>

typedef struct 
{
    int id;
    char name[16];
    char varient[16];
}poker_t;

#define _cardset_size sizeof(std_cards)/sizeof(poker_t)

typedef struct
{
    int id;

}player_t;


poker_t std_cards[] = {
    {1,"Heart","Ace"},
    {2,"Heart","2"},
    {3,"Heart","3"},
    {4,"Heart","4"},
    {5,"Heart","5"},
    {6,"Heart","6"},
    {7,"Heart","7"},
    {8,"Heart","8"},
    {9,"Heart","9"},
    {10,"Heart","10"},
    {11,"Heart","Jack"},
    {12,"Heart","King"},
    {13,"Heart","Queen"},
    {14,"Spades","Ace"},
    {15,"Spades","2"},
    {16,"Spades","3"},
    {17,"Spades","4"},
    {18,"Spades","5"},
    {19,"Spades","6"},
    {20,"Spades","7"},
    {21,"Spades","8"},
    {22,"Spades","9"},
    {23,"Spades","10"},
    {24,"Spades","Jack"},
    {25,"Spades","King"},
    {26,"Spades","Queen"},
    {27,"Diamonds","Ace"},
    {28,"Diamonds","2"},
    {29,"Diamonds","3"},
    {30,"Diamonds","4"},
    {31,"Diamonds","5"},
    {32,"Diamonds","6"},
    {33,"Diamonds","7"},
    {34,"Diamonds","8"},
    {35,"Diamonds","9"},
    {36,"Diamonds","10"},
    {37,"Diamonds","Jack"},
    {38,"Diamonds","King"},
    {39,"Diamonds","Queen"},
    {40,"Clubs","Ace"},
    {41,"Clubs","2"},
    {42,"Clubs","3"},
    {43,"Clubs","4"},
    {44,"Clubs","5"},
    {45,"Clubs","6"},
    {46,"Clubs","7"},
    {47,"Clubs","8"},
    {48,"Clubs","9"},
    {49,"Clubs","10"},
    {50,"Clubs","Jack"},
    {51,"Clubs","King"},
    {52,"Clubs","Queen"},
    {53,"Joker","A"},
    {54,"Joker","B"}
};


int players_cnt = 2;
duList_t *players_cards = NULL;

stack_t table;


int shuffle_sed = 6147;

void card_distribute()
{
    
    
    int average_cnt = (_cardset_size + players_cnt) / players_cnt;
    for(int i = 0; i < _cardset_size; ++ i)
    {
        int playerio = i / average_cnt;
        poker_t * p = &std_cards[i];
        duList_insert(&players_cards[playerio],0,&p);
    }
} // 发牌


void card_shuffler()
{
    poker_t tmp;
    long long int scl = 12949213;
    long long int k = 1021;
    long long int l = 4091;
    long long int a = 4;
    long long int b = 2421;
    for(int _sf = 0; _sf < 21; ++ _sf)
    {
        for(int i = 0; i < _cardset_size; ++ i)
        {
            k = ((a * k * k % scl + b * k % scl + shuffle_sed) % scl + scl) % scl;
            l = ((a * l * l % scl + b * l % scl + shuffle_sed) % scl + scl) % scl;
            int sl = l % _cardset_size;
            int sk = k % _cardset_size;
            if (l != k)
            {
                tmp = std_cards[sl];
                std_cards[sl] = std_cards[sk];
                std_cards[sk] = tmp; 
            }
        }
    }

} // 随机交换数字


void init()
{

    stack_init(&table, sizeof(poker_t*), NULL);
    players_cards = (duList_t *)malloc(sizeof(duList_t) * players_cnt);
    for(int i = 0; i < players_cnt; ++ i)
    {
        duList_init(&players_cards[i],sizeof(poker_t*),NULL);
    }

    const char *mots = 
    "====================================================================== \n"\
    "      Task 2_2 :          La Dache Emulator:                           \n"\
    "---------------------------------------------------------------------- \n"\
    "  Total Players : %d\n"\
    "  Shuffle Seed  : %u\n"\
    "  Usage: in terminal: \"[LaDache].exe <player counts> <ramdom seed>\""
    "====================================================================== \n";
    printf(mots, players_cnt, shuffle_sed);
    if( _cardset_size % players_cnt != 0)
    {
        printf(
            "Warn: Player quantity Can't divide cards quantity, the game might be unfair!\n"
        );
    }
}

void inspect(int rounds)
{
    printf( "  Rounds %d Starts!\n", rounds);

    printf("\n\n  Table Stack:\n");
    for(int i = 0; i < table._len; ++ i)
    {
        poker_t * p = ((poker_t **)table.data)[i]; 
        printf("[%s %s] ", p->name, p->varient);
    }

    for(int i =0; i < players_cnt; ++ i)
    {
        printf("\n\n  Player %d 's pocket:\n", i + 1);
        duList_node_t *p = (&players_cards[i])->_header->next;
        while (p != (&players_cards[i])->_header)
        {
            printf("[%s %s] ", (*(poker_t **)(p->data))->name, (*(poker_t **)(p->data))->varient);
            p = p->next;
        }
    }

    const char * end =
    "\n----------------------------------------------------------------------\n\n";
    printf(end);
}

void uninit()
{
    stack_uninit(&table);
    for(int i = 0; i < players_cnt; ++ i)
    {
        duList_uninit(&players_cards[i]);
    }
    free(players_cards);
}

int main(int argc, char * argv[])
{
    unsigned char running = 1;
    if(argc > 1)
    {
        int vio = sscanf(argv[1],"%d",&players_cnt);
        if(vio != 1 || players_cnt > _cardset_size || players_cnt < 1)
        {
            printf("fatal: Invalid Player quantity.\n");
            running = 0;
            system("pause");
        }
        
    }

    if(argc > 2)
    {
        int vio = sscanf(argv[2],"%d",&shuffle_sed);
        if(vio != 1)
        {
            printf("fatal: Invalid shuffle seed.\n");
            running = 0;
            system("pause");
        }
    }

    
    init();

    if(running)
    {

        card_shuffler();

        card_distribute();

        int round_counter = 1;

        unsigned char playing = 1;
        while (playing)
        {
        
            printf("\n\n===================[round inspection]================================== \n");

            ++ round_counter;
            inspect(round_counter);

            for(int i = 0; i < players_cnt; ++ i)
            {
                poker_t * p = NULL;
                

                if(duList_is_empty(&players_cards[i]))
                {
                    printf("player %d has no cards at first. unfair game!", i+1);
                    playing = 0;
                    break;
                }

                duList_get(&players_cards[i],0,&p);
                printf("player %d puts %s %s.\n", 1+i, p->name, p->varient);
                duList_erase(&players_cards[i],0);
                /*
                    对桌子上的牌查重

                    事实上这里可以使用键值对找到对应的
                    牌的位置的，但是为了使用数据结构，
                    采用了朴素的模拟思路。
                */
                int idx = -1;
                for(int j = 0; j < table._len; ++ j)
                {
                    if(0==strcmp(((poker_t**)table.data)[j]->varient, p->varient))
                    {
                        idx = j;
                    }
                }

                // 把自己手里的牌放下
                *(poker_t**)stack_push_get(&table) = p; 
              

                // 拉大车
                if(idx != -1)
                {
                    printf("Player %d La Dache!\n", i + 1);
                    while(table._len > idx)
                    {
                        poker_t* cardgot = *(poker_t **)stack_top(&table);
                        printf(" > Get %s %s\n",cardgot->name,cardgot->varient);
                        duList_insert_reverse(&players_cards[i],0,stack_top(&table));
                        stack_pop(&table);
                    }
                    printf("Stop La Dache.\n", i + 1);
                }
  

                if(duList_is_empty(&players_cards[i]))
                {
                    printf("GAME OVER : player %d Lose the game. game over!", i+1);
                    playing = 0;
                    break;
                }

            }

            printf("\n\n===================[round inspection]================================== \n");
            system("pause");
            system("cls");
        }
        
    }

    uninit();

    return 0;
}
